import { defineComponent, markRaw, onUnmounted, ref, watch } from 'vue'
import DataNode from '../../ItemNode/DataNode'
import {
  ElButton,
  ElColorPicker,
  ElForm,
  ElFormItem,
  ElIcon,
  ElInput,
  ElInputNumber,
  ElPopover,
  ElRadioButton,
  ElRadioGroup,
  ElSwitch,
  ElTag,
  ElText
} from 'element-plus'
import classes from '../../ItemNode/ItemNode.module.css'
import { uploadFile, file2Base64, debounce } from '../../utils'
import { Edit } from '@element-plus/icons-vue'
import Model from './Model.vue'
import ModelRaw from './Model.vue?raw'
import { state } from '../../store/store.js'
import CodeEditor from '../../panel/CodeEditor.vue'
import Tip from '../../panel/Tip.vue'
import { editing } from '../../store/store.js'
import request from '../../request/request'

export default class NModel extends DataNode {
  isNModel = true
  is3DNode = true

  constructor(node?: NModel) {
    super(node)

    this.watchStateOptions.push({
      value: 'initInfoData',
      label: '刷新信息数据'
    })

    if (!node) {
      this.path = './components/NModel/NModel.tsx'
      this.name = '模型'

      const style = this.props.style
      this.props.style = {
        ...style,
        width: '300px',
        height: '200px',
        flexShrink: '0'
      }

      this.props.dataUrl = ''
      this.props.dataFn = `function () {
  // 模型数据
  return [
    {
      name: String,
      url: String
    }
  ]
}`

      this.props.infoData = {}
      this.props.infoDataUrl = ''
      this.props.infoDataFn = `function () {
  return {
    modelName: '<div></div>'
  }
}`

      this.props.selectFn = `function (value) {\n  value\n}`

      this.props.option = {
        camera: {
          position: [10, 10, 10]
        },
        directionalLight: {
          position: [10, 50, 10],
          color: '#fff',
          intensity: 5,
          castShadow: false,
          shadowCameraRadius: 100,
          shadowHelperVisible: false
        },
        ambientLight: {
          color: '#404040',
          intensity: 1
        },
        orbitControls: {
          target: [0, 0, 0]
        },
        transformControls: {
          enabled: false,
          mode: 'translate'
        },
        models: []
      }
    }

    this.props.option.transformControls.enabled = editing.value

    const pComponent = this.component
    this.component = markRaw(
      defineComponent(
        (props) => {
          // this.props.data继承自DataNode
          watch(
            () => this.props.data,
            () => {
              if (this.props.dataFnable) this.props.option.models = this.props.data ?? []
            },
            { immediate: true }
          )
          // 信息数据
          const initInfoData = async () => {
            let resData = null

            if (this.props.infoDataUrl) {
              await request({
                url: this.props.infoDataUrl.replace(/\:(\w+)/g, (match, p1) => state[p1]),
                method: 'GET'
              }).then((res) => (resData = res))
            }

            if (this.props.infoDataFn) {
              try {
                if (state || resData) this.props.infoData = eval(`(${this.props.infoDataFn})`)()
              } catch (e) {}
            }
          }

          const initInfoDataDebounce = debounce(() => {
            initInfoData()
          }, 1000)

          watch([() => this.props.infoDataUrl, () => this.props.infoDataFn], initInfoDataDebounce)

          initInfoData()

          const watchStateSet = new Set()
          watch(
            () => this.props.watchState,
            () => {
              watchStateSet.forEach((v: () => void) => v())

              this.props.watchState.forEach((watchObj: watchStateObj) => {
                if (watchObj.type == 'initInfoData') {
                  watchStateSet.add(watch(() => state[watchObj.stateProperty], initInfoData))
                }
              })
            },
            { immediate: true, deep: true }
          )
          onUnmounted(() => {
            watchStateSet.forEach((v: () => void) => v())
          })

          const onSelect = (value) => {
            if (state) eval(`(${this.props.selectFn})`)(value)
          }

          return () => (
            <pComponent>
              <Model
                option={this.props.option}
                infoData={this.props.infoData}
                onSelect={onSelect}
              ></Model>
            </pComponent>
          )
        },
        {
          name: 'NModel'
        }
      )
    )

    const pView = this.view
    this.view = markRaw(
      defineComponent((props) => {
        this.props.dataFn = ''
        this.props.option.models = []

        return () => (
          <pView>
            <this.component style={{ transform: 'scale(0.35, 0.3)' }}></this.component>
          </pView>
        )
      })
    )

    const pParams = this.params
    this.params = markRaw(
      defineComponent(() => {
        return () => (
          <pParams>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                模型
              </ElText>
              <div class={classes['params-item-content']}>
                <ElButton
                  type="primary"
                  size="small"
                  onClick={() => {
                    const item = {
                      name: 'defaultName',
                      url: ''
                    }
                    this.props.option.models.push(item)
                  }}
                >
                  + 添加模型
                </ElButton>
                <br></br>
                {this.props.option.models.map((item, index) => (
                  <ElTag
                    key={item.name}
                    closable
                    type="primary"
                    onClose={() => this.props.option.models.splice(index, 1)}
                  >
                    {{
                      default: () => {
                        return (
                          <span
                            style={{
                              flex: '1',
                              display: 'flex',
                              justifyContent: 'space-between'
                            }}
                          >
                            <span>{item.name}</span>
                            <span>
                              <ElPopover
                                width={300}
                                trigger="click"
                                teleported={false}
                                placement="top"
                              >
                                {{
                                  reference: () => (
                                    <ElIcon color="var(--el-color-primary)">
                                      <Edit></Edit>
                                    </ElIcon>
                                  ),
                                  default: () => {
                                    // 循环组件有key，修改值会刷新
                                    const name = ref(item.name)

                                    return (
                                      <>
                                        <ElForm label-width="60px">
                                          <ElFormItem label="模型地址">
                                            <ElInput
                                              v-model={item.url}
                                              size="small"
                                              clearable
                                            ></ElInput>
                                            <ElButton
                                              type="primary"
                                              size="small"
                                              onClick={() => {
                                                uploadFile((file) => {
                                                  file2Base64(file).then((res) => (item.url = res))
                                                })
                                              }}
                                            >
                                              选择模型
                                            </ElButton>
                                          </ElFormItem>
                                          <ElFormItem label="模型名">
                                            <ElInput
                                              v-model={name.value}
                                              onChange={(v) => (item.name = v)}
                                              size="small"
                                            ></ElInput>
                                          </ElFormItem>
                                        </ElForm>
                                      </>
                                    )
                                  }
                                }}
                              </ElPopover>
                            </span>
                          </span>
                        )
                      }
                    }}
                  </ElTag>
                ))}
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                选中模型处理函数
                <Tip content={'可访问全局状态变量state，value：选中的模型对象'}></Tip>
              </ElText>
              <div class={classes['params-item-content']}>
                <CodeEditor
                  modelValue={this.props.selectFn}
                  onUpdate:modelValue={(v) => (this.props.selectFn = v || '')}
                ></CodeEditor>
              </div>
            </div>

            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                信息url
                <Tip content={'可引用全局状态变量state的属性，例子：/abc/:def引用state.def'}></Tip>
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInput
                  modelValue={this.props.infoDataUrl}
                  onUpdate:modelValue={(v) => (this.props.infoDataUrl = v || '')}
                  size="small"
                ></ElInput>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                信息函数
                <Tip content={'可访问的变量：全局状态变量state，接口返回的数据resData'}></Tip>
              </ElText>
              <div class={classes['params-item-content']}>
                <CodeEditor
                  modelValue={this.props.infoDataFn}
                  onUpdate:modelValue={(v) => (this.props.infoDataFn = v || '')}
                ></CodeEditor>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                轨道控制器焦点坐标
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInputNumber
                  v-model={this.props.option.orbitControls.target[0]}
                  size="small"
                  controls={false}
                >
                  {{ prefix: () => <span style="color: var(--el-color-primary)">x</span> }}
                </ElInputNumber>
                <ElInputNumber
                  v-model={this.props.option.orbitControls.target[1]}
                  size="small"
                  controls={false}
                >
                  {{ prefix: () => <span style="color: var(--el-color-primary)">y</span> }}
                </ElInputNumber>
                <ElInputNumber
                  v-model={this.props.option.orbitControls.target[2]}
                  size="small"
                  controls={false}
                >
                  {{ prefix: () => <span style="color: var(--el-color-primary)">z</span> }}
                </ElInputNumber>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                相机坐标坐标
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInputNumber
                  v-model={this.props.option.camera.position[0]}
                  size="small"
                  controls={false}
                >
                  {{ prefix: () => <span style="color: var(--el-color-primary)">x</span> }}
                </ElInputNumber>
                <ElInputNumber
                  v-model={this.props.option.camera.position[1]}
                  size="small"
                  controls={false}
                >
                  {{ prefix: () => <span style="color: var(--el-color-primary)">y</span> }}
                </ElInputNumber>
                <ElInputNumber
                  v-model={this.props.option.camera.position[2]}
                  size="small"
                  controls={false}
                >
                  {{ prefix: () => <span style="color: var(--el-color-primary)">z</span> }}
                </ElInputNumber>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                变换控制器
              </ElText>
              <div class={classes['params-item-content']}>
                <ElSwitch
                  v-model={this.props.option.transformControls.enabled}
                  size="small"
                ></ElSwitch>
              </div>
            </div>
            {this.props.option.transformControls.enabled ? (
              <div class={classes['params-item']}>
                <ElText class={classes['params-item-label']} type="info">
                  变换模式
                </ElText>
                <div class={classes['params-item-content']}>
                  <ElRadioGroup v-model={this.props.option.transformControls.mode}>
                    <ElRadioButton value="translate" label="平移" size="small"></ElRadioButton>
                    <ElRadioButton value="rotate" label="旋转" size="small"></ElRadioButton>
                    <ElRadioButton value="scale" label="缩放" size="small"></ElRadioButton>
                  </ElRadioGroup>
                </div>
              </div>
            ) : null}
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                平行光位置
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInputNumber
                  v-model={this.props.option.directionalLight.position[0]}
                  size="small"
                  controls={false}
                >
                  {{ prefix: () => <span style="color: var(--el-color-primary)">x</span> }}
                </ElInputNumber>
                <ElInputNumber
                  v-model={this.props.option.directionalLight.position[1]}
                  size="small"
                  controls={false}
                >
                  {{ prefix: () => <span style="color: var(--el-color-primary)">y</span> }}
                </ElInputNumber>
                <ElInputNumber
                  v-model={this.props.option.directionalLight.position[2]}
                  size="small"
                  controls={false}
                >
                  {{ prefix: () => <span style="color: var(--el-color-primary)">z</span> }}
                </ElInputNumber>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                平行光颜色
              </ElText>
              <div class={classes['params-item-content']}>
                <ElColorPicker
                  modelValue={this.props.option.directionalLight.color}
                  onUpdate:modelValue={(v) => (this.props.option.directionalLight.color = v || '')}
                  show-alpha
                  size="small"
                  teleported={false}
                ></ElColorPicker>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                平行光强度
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInputNumber
                  v-model={this.props.option.directionalLight.intensity}
                  min={1}
                  size="small"
                ></ElInputNumber>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                平行光阴影
              </ElText>
              <div class={classes['params-item-content']}>
                <ElSwitch
                  v-model={this.props.option.directionalLight.castShadow}
                  size="small"
                ></ElSwitch>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                平行光阴影辅助
              </ElText>
              <div class={classes['params-item-content']}>
                <ElSwitch
                  v-model={this.props.option.directionalLight.shadowHelperVisible}
                  size="small"
                ></ElSwitch>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                平行光阴影半径
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInputNumber
                  v-model={this.props.option.directionalLight.shadowCameraRadius}
                  min={1}
                  size="small"
                ></ElInputNumber>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                环境光颜色
              </ElText>
              <div class={classes['params-item-content']}>
                <ElColorPicker
                  modelValue={this.props.option.ambientLight.color}
                  onUpdate:modelValue={(v) => (this.props.option.ambientLight.color = v || '')}
                  show-alpha
                  size="small"
                  teleported={false}
                ></ElColorPicker>
              </div>
            </div>
            <div class={classes['params-item']}>
              <ElText class={classes['params-item-label']} type="info">
                环境光强度
              </ElText>
              <div class={classes['params-item-content']}>
                <ElInputNumber
                  v-model={this.props.option.ambientLight.intensity}
                  min={0.1}
                  size="small"
                ></ElInputNumber>
              </div>
            </div>
          </pParams>
        )
      })
    )
  }

  toVue(): SfcObj {
    const sfcObj = super.toVue()
    const name = sfcObj.name
    const dataVar = sfcObj.dataVar
    const [uuid0, uuid1] = this.uuid.split('-')
    const optionVar = `${name}_${uuid0}_option`
    const onSelectVar = `${name}_${uuid0}_onSelect`

    const optionStr = JSON.stringify(this.props.option)

    sfcObj.imports = {
      ...sfcObj.imports,
      vue: {
        ...sfcObj.imports.vue,
        ref: 'ref',
        watch: 'watch'
      },
      ['./imports/Model.vue']: {
        default: 'Model'
      }
    }

    sfcObj.importFiles = {
      ...sfcObj.importFiles,
      'Model.vue': ModelRaw
    }

    sfcObj.script = `
${sfcObj.script}
const ${optionVar} = ref(${optionStr})
watch(${dataVar}, () => {
  ${optionVar}.value.models = ${dataVar}.value ?? []
})
const ${onSelectVar} = (value) => {
  (${this.props.selectFn})(value)
}
      `

    sfcObj.template = sfcObj.template.replace(
      '<slot></slot>',
      `<Model :option="${optionVar}" @select="${onSelectVar}" ></Model>`
    )

    return sfcObj
  }
}
